from tkinter import *
from tkinter.ttk import *
from tkinter.filedialog import *
from tkinter.messagebox import *
from tkinter.font import *
from ttkthemes import ThemedTk
from chardet import detect

#创建窗口
root = ThemedTk()
root.title("无标题 - Notepad")
root.geometry(f"600x400+{(root.winfo_screenwidth() - 600)//2}+{(root.winfo_screenheight() - 400)//2}")
root.set_theme(theme_name = "arc", themebg = True)

#创建编辑区，滚动条
text = Text(root, undo = True, wrap = "none")
xscroll = Scrollbar(root, orient = HORIZONTAL)
yscroll = Scrollbar(root)
xscroll.pack(side = BOTTOM, fill = X)
yscroll.pack(side = RIGHT, fill = Y)
xscroll.config(command = text.xview)
yscroll.config(command = text.yview)
text.config(xscrollcommand = xscroll.set, yscrollcommand = yscroll.set)
text.config(font = Font(size = 15, family = "Times", weight = "bold"))
text.pack(fill = BOTH, expand = True)
text.bind("<Key>", lambda event: text.edit_separator())

#定义
doc = ""
filename = ""

def newfile():
    if asksaved():
        text.delete("1.0", END)
        root.title("无标题 - Notepad")
def openfile():
    global doc, filename
    filename = askopenfilename(title = "打开文件", initialdir = "C://Users/", filetypes = [("文本文件(*.txt)", "*.txt"), ("所有文件", "*.*")])
    if filename:
        root.title(filename + " - Notepad")
        text.delete("1.0", END)
        with open(filename, "rb") as fb: code = detect(fb.read())["encoding"]
        with open(filename, "r", encoding = code) as f: doc = "".join(f.readlines())
        text.insert("1.0", doc)
        doc = (" " if doc == "" else doc)
def save():
    global doc
    if doc == "":
        saveas()
    else:
        doc = text.get("1.0", END)
        with open(filename, "w", encoding = "utf-8", errors = "replace") as f:
            f.write(doc) if f.writable() else showerror("错误", "文件保存失败")
def saveas():
    global filename, doc
    filename = asksaveasfilename(title = "另存为", initialdir = "C://Users/", filetypes = [("文本文件(*.txt)", "*.txt"), ("所有文件", "*.*")])
    if filename:
        if filename.find(".") == -1:
            filename += ".txt"
        root.title(filename + " - Notepad")
        doc = text.get("1.0", END)
        with open(filename, "w", encoding = "utf-8", errors = "replace") as f:
            f.write(doc) if f.writable() else showerror("错误", "文件保存失败")
def asksaved():
    if doc == text.get("1.0", END):
        return True
    else:
        result = askyesnocancel("离开前保存", "是否保存当前文件?")
        if result == True:
            save()
            return True
        elif result == False: return True
        return False
def askclosewindow():
    if asksaved():
        root.destroy()
def undo():
    try:
        text.edit_undo()
    except:
        root.bell()
def redo():
    try:
        text.edit_redo()
    except:
        root.bell()
def copy():
    try:
        copytext = text.get(SEL_FIRST, SEL_LAST)
        text.clipboard_clear()
        text.clipboard_append(copytext)
    except:
        root.bell()
def cut():
    try:
        copy()
        text.delete(SEL_FIRST, SEL_LAST)
    except:
        root.bell()
def paste():
    try:
        pastetext = text.clipboard_get()
        text.insert(INSERT, pastetext)
    except:
        root.bell()
def showpopmenu(event):
    popmenu.post(event.x, event.y)

#创建菜单
#主菜单
menubar = Menu(root)
##"文件"菜单
filemenu = Menu(menubar, tearoff = False)
menubar.add_cascade(label = "文件", menu = filemenu)
filemenu.add_command(label = "新建文件", command = newfile, accelerator = "Ctrl+N")
filemenu.add_command(label = "打开文件", command = openfile, accelerator = "Ctrl+O")
filemenu.add_separator()
filemenu.add_command(label = "保存文件", command = save, accelerator = "Ctrl+S")
filemenu.add_command(label = "另存为", command = saveas, accelerator = "Ctrl+Shift+S")
filemenu.add_separator()
filemenu.add_command(label = "退出", command = askclosewindow, accelerator = "Alt+F4")
##"编辑"菜单
editmenu = Menu(menubar, tearoff = False)
menubar.add_cascade(label = "编辑", menu = editmenu)
editmenu.add_command(label = "撤销", command = undo, accelerator = "Ctrl+Z")
editmenu.add_command(label = "恢复", command = redo, accelerator = "Ctrl+Y")
editmenu.add_separator()
editmenu.add_command(label = "剪切", command = cut, accelerator = "Ctrl+X")
editmenu.add_command(label = "复制", command = copy, accelerator = "Ctrl+C")
editmenu.add_command(label = "粘贴", command = paste, accelerator = "Ctrl+V")
#右键菜单
popmenu = Menu(root, tearoff = False)
popmenu.add_command(label = "剪切", command = cut)
popmenu.add_command(label = "复制", command = copy)
popmenu.add_command(label = "粘贴", command = paste)

root.config(menu = menubar)  #显示菜单
root.bind("<Button-3>", showpopmenu)
#绑定事件
root.bind("<Control-N>", lambda event: newfile())
root.bind("<Control-O>", lambda event: openfile())
root.bind("<Control-S>", lambda event: save())
root.bind("<Control-Shift-S>", lambda event: saveas())
root.bind("<Control-X>", lambda event: cut())
root.bind("<Control-C>", lambda event: copy())
root.bind("<Control-V>", lambda event: paste())
root.protocol("WM_DELETE_WINDOW", askclosewindow)
#主循环
root.mainloop()